#define _EKOS_KERNEL_C_

#include <kernel/kernel.h>

static inline int      pcs_push(uint32_t i);
static inline uint32_t pcs_pop(void);
static inline uint32_t pcs_head(void);

/* int */
/* pcs_init(void) */
/* { */
/*      physaddr_t pcs_addr; */
/*      int err; */
/*      if ((err = frame_alloc(&pcs_addr)) != 0) */
/* 		  return err; */
     
/*      frame_inc_ref(pcs_addr); */
/*      vpt[PAGE_NUM(&pcs)] = pcs_addr | PTE_W | PTE_P; */
     
/*      tlbflush(); */
/*      pcs_init_struct(&pcs); */

/*      return 0; */
/* } */

void
pcs_init_struct(volatile struct pcs_t *pcs)
{
     pcs->last  = 0;
     pcs->count = 0;
     pcs->total = 0;
}

int
pcs_destroy(void)
{
	 volatile struct pcs_t *pcs = get_local_pcs();
	 
     physaddr_t tmp;
     while (true)
     {
		  tmp = vpt[PAGE_NUM(pcs)];
     
		  if (tmp == 0) break;
		  vpt[PAGE_NUM(pcs)] = pcs->last;
		  tlbflush();
         
		  frame_dec_ref(tmp);
     }

     return 0;
}

int
pcs_push(uint32_t i)
{
	 volatile struct pcs_t *pcs = get_local_pcs();
	 
     if (pcs->count == PCS_PAGE_COUNT)
     {
		  physaddr_t new_pcs;
		  int err;
	  
		  if ((err = frame_alloc(&new_pcs)) != 0)
			   return err;

		  frame_inc_ref(new_pcs);
		  uintptr_t total = pcs->total + 1;
	  
		  physaddr_t last = vpt[PAGE_NUM(pcs)];
		  vpt[PAGE_NUM(pcs)] = new_pcs | PTE_W | PTE_P;
	  
		  tlbflush();

		  pcs->last = last;
		  pcs->count = 1;
		  pcs->total = total;
		  pcs->data[0] = i;
     }
     else
     {
		  pcs->data[pcs->count] = i;
		  ++pcs->count;
		  ++pcs->total;
     }

     return 0;
}

uint32_t
pcs_pop(void)
{
	 volatile struct pcs_t *pcs = get_local_pcs();
	 
     uint32_t result;
     result = pcs->data[--pcs->count];

     if (pcs->count == 0 && pcs->last != 0)
     {
		  physaddr_t tmp;
		  tmp = vpt[PAGE_NUM(pcs)];
		  vpt[PAGE_NUM(pcs)] = pcs->last;

		  tlbflush();

		  frame_dec_ref(tmp);
     }
     else --pcs->total;

     return result;
}

uint32_t
pcs_head(void)
{
	 volatile struct pcs_t *pcs = get_local_pcs();
     return pcs->data[pcs->count - 1];
}

uint32_t
pcs_push_level(void)
{
	 volatile struct pcs_t *pcs = get_local_pcs();
     return pcs->total;
}

void
pcs_pop_level(uint32_t l)
{
	 volatile struct pcs_t *pcs = get_local_pcs();
     while (pcs->total > l)
		  pcs_pop();
}

void
pcs_clear_level(uint32_t l)
{
	 volatile struct pcs_t *pcs = get_local_pcs();
     physaddr_t addr;
     
     while (pcs->total > l)
     {
		  addr = pcs_pop();
		  frame_dec_ref(addr);
     }
}

int
pcs_push_frame(physaddr_t addr)
{
     return pcs_push(addr);
}
